package xyz.iguilty.workflow.core.service.impl;

import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import xyz.iguilty.workflow.constant.ProcessConstants;
import xyz.iguilty.workflow.core.service.WorkFlowInfoService;
import xyz.iguilty.workflow.model.*;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class FlowableInfoService implements WorkFlowInfoService {

    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RepositoryService repositoryService;

    @Override
    public PageInfo<TaskInfo> tasksTodo(TaskCriteria criteria, Integer pageNumber, Integer pageSize) {
        TaskQuery taskQuery = taskService.createTaskQuery().includeProcessVariables();
        if (criteria.getProcessDefinitionKey() != null) {
            taskQuery.processDefinitionKey(criteria.getProcessDefinitionKey());
        }
        if (criteria.getActor() == null) {
            throw new IllegalArgumentException("请输入用户id");
        }
        taskQuery.taskCandidateOrAssigned(criteria.getActor());
        long count = 0;
        List<Task> tasks = null;
        try {
            count = taskQuery.count();
            tasks = taskQuery.orderByTaskCreateTime().desc().listPage(pageNumber-1, pageSize);
        } catch (Exception exception) {
            return PageInfo.empty();
        }
        List<TaskInfo> taskInfoList = tasks.stream().map(task -> {
            TaskInfo taskInfo = new TaskInfo();
            taskInfo.setTaskId(task.getId());
            taskInfo.setCurrentActivityKey(task.getTaskDefinitionKey());
            taskInfo.setTaskName(task.getName());
            taskInfo.setTaskDescription(task.getDescription());
            taskInfo.setClaimTime(task.getClaimTime());
            taskInfo.setCreateTime(task.getCreateTime());
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).includeProcessVariables().singleResult();
            if (processInstance != null) {
                ProcessInstanceInfo processInstanceInfo = new ProcessInstanceInfo();
                processInstanceInfo.setBusinessKey(processInstance.getBusinessKey());
                processInstanceInfo.setProcessDefinitionKey(processInstance.getProcessDefinitionKey());
                processInstanceInfo.setProcessDefinitionName(processInstance.getProcessDefinitionName());
                processInstanceInfo.setStartTime(processInstance.getStartTime());
                processInstanceInfo.setStarterUserId(processInstance.getStartUserId());
                processInstanceInfo.setProcessInstanceId(processInstance.getId());
                processInstanceInfo.setProcessInstanceName(processInstance.getName());
                Map<String, Object> processVariables = processInstance.getProcessVariables();
                getInformationFromVariable(processInstanceInfo,processVariables);
                taskInfo.setProcessInstanceInfo(processInstanceInfo);
            }
            return taskInfo;
        }).collect(Collectors.toList());
        return PageInfo.build(taskInfoList, count);
    }

    @Override
    public PageInfo<TaskInfo> tasksResolved(TaskCriteria criteria, Integer pageNumber, Integer pageSize) {
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().finished();
        if (criteria.getActor() != null) {
            query.taskAssignee(criteria.getActor());
        }
        if (criteria.getProcessDefinitionKey()!=null){
            query.processDefinitionKey(criteria.getProcessDefinitionKey());
        }
        long count = query.count();
        List<HistoricTaskInstance> historicTaskInstances = query.orderByTaskCreateTime().desc().listPage(pageNumber - 1, pageSize);
        List<TaskInfo> taskInfoList = historicTaskInstances.stream().map(taskInstance -> {
            TaskInfo taskInfo = new TaskInfo();
            taskInfo.setTaskId(taskInstance.getId());
            taskInfo.setCurrentActivityKey(taskInstance.getTaskDefinitionKey());
            taskInfo.setTaskName(taskInstance.getName());
            taskInfo.setTaskDescription(taskInstance.getDescription());
            taskInfo.setClaimTime(taskInstance.getClaimTime());
            taskInfo.setCreateTime(taskInstance.getCreateTime());
            taskInfo.setEndTime(taskInstance.getEndTime());
            String processInstanceId = taskInstance.getProcessInstanceId();
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).includeProcessVariables().singleResult();
            if (historicProcessInstance != null) {
                ProcessInstanceInfo processInstanceInfo = new ProcessInstanceInfo();
                processInstanceInfo.setProcessInstanceId(processInstanceId);
                processInstanceInfo.setProcessDefinitionKey(historicProcessInstance.getProcessDefinitionKey());
                processInstanceInfo.setProcessDefinitionName(historicProcessInstance.getProcessDefinitionName());
                processInstanceInfo.setStartTime(historicProcessInstance.getStartTime());
                processInstanceInfo.setStarterUserId(historicProcessInstance.getStartUserId());
                processInstanceInfo.setBusinessKey(historicProcessInstance.getBusinessKey());
                Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
                getInformationFromVariable(processInstanceInfo,processVariables);
                taskInfo.setProcessInstanceInfo(processInstanceInfo);
            }
            return taskInfo;
        }).collect(Collectors.toList());
        return PageInfo.build(taskInfoList, count);
    }

    @Override
    public PageInfo<ProcessInstanceInfo> process(ProcessInstanceCriteria criteria, Integer pageNumber, Integer pageSize) {
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
        if (!ObjectUtils.isEmpty(criteria.getStartUserId())){
            query.startedBy(criteria.getStartUserId());
        }
        if (criteria.getProcessDefinitionKey()!=null){
            query.processDefinitionKey(criteria.getProcessDefinitionKey());
        }
        long count = query.count();
        List<HistoricProcessInstance> historicProcessInstances = query.orderByProcessInstanceStartTime().includeProcessVariables().desc().listPage(pageNumber - 1, pageSize);
        List<ProcessInstanceInfo> processInstanceInfoList = historicProcessInstances.stream().map(historicProcessInstance -> {
            ProcessInstanceInfo processInstanceInfo = new ProcessInstanceInfo();
            processInstanceInfo.setBusinessKey(historicProcessInstance.getBusinessKey());
            processInstanceInfo.setProcessDefinitionName(historicProcessInstance.getProcessDefinitionName());
            processInstanceInfo.setProcessDefinitionKey(historicProcessInstance.getProcessDefinitionKey());
            processInstanceInfo.setStartTime(historicProcessInstance.getStartTime());
            processInstanceInfo.setStarterUserId(historicProcessInstance.getStartUserId());
            processInstanceInfo.setProcessInstanceId(((HistoricProcessInstanceEntityImpl) historicProcessInstance).getProcessInstanceId());
            getInformationFromVariable(processInstanceInfo,historicProcessInstance.getProcessVariables());
            return processInstanceInfo;
        }).collect(Collectors.toList());
        return PageInfo.build(processInstanceInfoList,count);
    }

    public void getInformationFromVariable(ProcessInstanceInfo processInstanceInfo,Map<String,Object> variable){
        if (processInstanceInfo == null||variable==null) {
            return;
        }
        Object currentActor = variable.get(ProcessConstants.CURRENT_ACTOR);
        if (currentActor != null) {
            processInstanceInfo.setCurrentActor(currentActor.toString());
        }
        Object currentStatus = variable.get(ProcessConstants.CURRENT_STATUS);
        if (currentStatus != null) {
            processInstanceInfo.setCurrentStatus(currentStatus.toString());
        }
    }

    @Override
    public InputStream track(String processInstanceId) {
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = null;
        List<String> activeActivityIds = new ArrayList<>();
        List<String> highLightedFlows = new ArrayList<>();
        //2.获取所有的历史轨迹线对象
        List<HistoricActivityInstance> historicSequenceFlows = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW).list();
        historicSequenceFlows.forEach(historicActivityInstance -> highLightedFlows.add(historicActivityInstance.getActivityId()));
        //3. 获取流程定义id和高亮的节点id
        if (processInstance != null) {
            //3.1. 正在运行的流程实例
            processDefinitionId = processInstance.getProcessDefinitionId();
            activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        } else {
            //3.2. 已经结束的流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            //3.3. 获取结束节点列表
            List<HistoricActivityInstance> historicEnds = historyService.createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId).activityType(BpmnXMLConstants.ELEMENT_EVENT_END).list();
            List<String> finalActiveActivityIds = activeActivityIds;
            historicEnds.forEach(historicActivityInstance -> finalActiveActivityIds.add(historicActivityInstance.getActivityId()));
        }
        //4. 获取bpmnModel对象
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        //5. 生成图片流
        DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
        return generator.generateDiagram(bpmnModel, "png", activeActivityIds, highLightedFlows, "宋体", "宋体", "宋体", null, 1.0, true);
    }
}
